#pragma once

#include<iostream>
#include<vector>
#include<queue>



struct ThreadData
{
    pthread_t tid;
    std::string name;
};

template<class T>
class ThreadPool
{
    void lock()
    {
        pthread_mutex_lock(&mutex_);
    }

    void unLock()
    {
        pthread_mutex_unlock(&mutex_);
    }

    void wakeUp()
    {
        pthread_cond_signal(&cond_);
    }

    void threadSleep()
    {
        pthread_cond_wait(&cond_,&mutex_);
    }


    static const int defaultnum = 4;
public:
    ThreadPool(int num = defaultnum)
    :threads(num)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&cond_,nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    bool isEmpty()
    {
        return tasks.size() == 0;
    }

    static void* handlerTask(void* args)
    {
        ThreadPool<T>* tp = static_cast<ThreadPool<T>*>(args);
        std::string name = tp->getThreadName(pthread_self());

        while(true)
        {
            tp->lock();
            while(tp->isEmpty())
            {
                tp->threadSleep();
            }
            T tmp = tp->pop();
            tp->unLock();
            tmp();
            std::cout << name <<"has done task, result is :" << tmp.getResult() << std::endl;

        }
    }

    std::string getThreadName(const pthread_t& tid)
    {
        for(int i = 0;i < threads.size();i++)
        {
            if(tid == threads[i].tid) return threads[i].name;
        }
        return "";
    }

    void strart()
    {
        for(int i = 0;i < threads.size();i++)
        {
            threads[i].name = "thread--" + std::to_string(i);

            pthread_create(&(threads[i].tid),nullptr,handlerTask,this);
        }
    }

    void push(const T& t)
    {
        lock();
        tasks.push(t);
        wakeUp();
        unLock();
    }

    T pop()
    {
        T tmp = tasks.front();
        tasks.pop();
        return tmp;
    }


private:
    std::vector<ThreadData> threads;
    std::queue<T> tasks;

    pthread_mutex_t mutex_;
    pthread_cond_t cond_;

};